﻿using System;
using System.Activities;
using System.Activities.Statements;
using Headsight.BPMN2WF.BPMN.Model;
using Headsight.BPMN2WF.BPMN.Utils;
using Microsoft.VisualBasic.Activities;

namespace Headsight.BPMN2WF.Transformation.Transformers
{
    internal class BPMN2WFTransformationInclusiveGateway : IBPMN2WFTransformation
    {
        private Sequence _result;

        public BPMNTransformationResult Transform(IBPMN2WFTransformationContext ctx, BPMNFlowNode flowNode)
        {
            var processBlock = (BPMNFlowNodesBlock)flowNode;

            _result = new Sequence();

            _result.Variables.Add(new Variable<bool>("DefaultBranch") { Default = new VisualBasicValue<bool>("True") });

            foreach (var each in processBlock.GetNextNodesInFlowFrom(processBlock.FlowNodeIn))
            {
                if (!ctx.BPMNProcess.IsDefaultBranchOf(processBlock.FlowNodeIn, each.BPMNFlowNodeOrNodeIn()))
                {
                    var transformed = TransformInternal(ctx, processBlock, each);
                    _result.Activities.Add(transformed);
                }
            }

            if (!ctx.BPMNProcess.HasDefaultBranch(processBlock.FlowNodeIn))
            {
                _result.Activities.Add(CreateDefaultBranchThrowingException());
            }
            else
            {
                var flowNodeDefaultBranchOrgModel = ctx.BPMNProcess.GetDefaultBranch(processBlock.FlowNodeIn);
                var flowNodeDefaultBranch = processBlock.GetBPMNFlowNodeById(flowNodeDefaultBranchOrgModel.Id) ??
                      processBlock.GetBPMNFlowNodeById("block_" + flowNodeDefaultBranchOrgModel.Id);


                _result.Activities.Add(CreateDefaultBranch(ctx, flowNodeDefaultBranch));
            }

            return new BPMNTransformationResult(_result, _result.Activities[0], _result.Activities[_result.Activities.Count - 1]);
        }

        private Activity TransformInternal(IBPMN2WFTransformationContext ctx, BPMNFlowNodesBlock processBlock, BPMNFlowNode each)
        {
            var expression = ctx.BPMNProcess.GetExpressionIdAndExpression(processBlock.FlowNodeIn, each.BPMNFlowNodeOrNodeIn());

            var ifAct = CreateIfActivity(expression);

            ifAct.Then = new Sequence();
            ((Sequence)ifAct.Then).Activities.Add(CreateSetSetDefaultFalse());

            var activity = CreateActivity(ctx, each);

            ((Sequence)ifAct.Then).Activities.Add(activity);


            return ifAct;
        }

        private Activity CreateDefaultBranch(IBPMN2WFTransformationContext ctx, BPMNFlowNode each)
        {
            var ifAct1 = new If
                             {
                                 Condition = new InArgument<bool>
                                                 {
                                                     Expression = new VisualBasicValue<bool>("DefaultBranch = True")
                                                 }
                             };
            var ifAct = ifAct1;

            ifAct.Then = CreateActivity(ctx, each);
            return ifAct;
        }


        private static If CreateIfActivity(Tuple<string, string> expression)
        {
            var ifAct = new If
                            {
                                Condition = new InArgument<bool>
                                                {
                                                    Expression = new VisualBasicValue<bool>(expression.Item2)
                                                }
                            };
            return ifAct;
        }

        private Activity CreateActivity(IBPMN2WFTransformationContext ctx, BPMNFlowNode each)
        {
            // Recursive? Chidren?
            return ctx.BPMN2WFTransformations.Get(each).Transform(ctx, each).Result;
        }

        private static Activity CreateDefaultBranch(Activity activity)
        {
            var result = new If
                             {
                                 Condition = new InArgument<bool>
                                                 {
                                                     Expression = new VisualBasicValue<bool>("DefaultBranch")
                                                 },
                                 Then = activity
                             };

            return result;
        }

        private static Activity CreateDefaultBranchThrowingException()
        {
            var result = new If
                             {
                                 Condition = new InArgument<bool>
                                                 {
                                                     Expression = new VisualBasicValue<bool>("DefaultBranch")
                                                 },
                                 Then = new Throw
                                            {
                                                Exception =
                                                    new InArgument<Exception>(
                                                    new VisualBasicValue<Exception>(
                                                        "New System.Exception(\"Next flow step could not be determined and BPMN source model has not defined any default branch. BPMN execution semantics require to throw this exception.\")"))
                                            }
                             };

            return result;
        }

        private static Activity CreateSetSetDefaultFalse()
        {
            return new Assign
             {
                 To = new OutArgument<Boolean>
                     {
                         Expression = new VisualBasicReference<Boolean>
                         {
                             ExpressionText = "DefaultBranch"
                         }
                     },
                 Value = new InArgument<bool>
                 {
                     Expression = new VisualBasicValue<bool>(String.Format("False"))
                 }
             };
        }
    }
}
